package com.lean.pulltorefresh.list.base;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;

/**
 * Created by Lean·He on 16/7/28.
 */
public abstract class PullToRefreshView extends ListView {

    public static final int RELEASE_TO_REFRESH = 0;
    public static final int PULL_TO_REFRESH = 1;
    public static final int REFRESHING = 2;
    public static final int RESET = 3;
    public static final int LOAD_MORE = 4;
    public static final int RATIO = 2;
    public final String TAG = "PullToRefreshView";
    private boolean isPullToRefreshing;
    private int lastMotionY;
    private int state;
    private OnRefreshListener refreshListener;
    private OnLoadMoreListener onLoadMoreListener;
    private OnScrollListener otherScrollListener;
    private final OnScrollListener onScrollListener = new OnScrollListener() {
        boolean isInBottom = false;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (otherScrollListener != null) {
                otherScrollListener.onScrollStateChanged(view, scrollState);
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (otherScrollListener != null) {
                otherScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
            }
            isInBottom = (firstVisibleItem + visibleItemCount) == totalItemCount;
            if (onLoadMoreListener != null && isInBottom && state != LOAD_MORE && visibleItemCount > 0) {
                setState(LOAD_MORE);
            }
        }
    };

    public PullToRefreshView(Context context) {
        this(context, null);
    }

    public PullToRefreshView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PullToRefreshView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnScrollListener(onScrollListener);
    }

    protected int getFirstVisibleViewTop() {
        View view = getChildAt(0);
        if (view == null) {
            return 0;
        }
        int top = -view.getTop();
        return top;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (getFirstVisiblePosition() == 0 && !isPullToRefreshing) {
                    isPullToRefreshing = true;
                    lastMotionY = (int) event.getY();
                }
                break;

            case MotionEvent.ACTION_UP:
                if (state != REFRESHING) {
                    if (state == PULL_TO_REFRESH) {
                        setState(RESET);
                    }
                    if (state == RELEASE_TO_REFRESH) {
                        setState(REFRESHING);
                    }
                }
                isPullToRefreshing = false;
                break;
            case MotionEvent.ACTION_MOVE:
                int y = (int) event.getY();
                int deltaY = (y - lastMotionY) / RATIO;
                if (!isPullToRefreshing && getFirstVisiblePosition() == 0) {
                    isPullToRefreshing = true;
                    lastMotionY = y;
                }
                if (state != REFRESHING && isPullToRefreshing) {
                    if (state == RELEASE_TO_REFRESH) {
                        if ((deltaY < isReadyToRefreshSize()) && deltaY > 0) {
                            setState(PULL_TO_REFRESH);
                        } else if (deltaY <= 0) {
                            setState(RESET);
                        }
                    }
                    if (state == PULL_TO_REFRESH) {
                        if (deltaY >= isReadyToRefreshSize()) {
                            setState(RELEASE_TO_REFRESH);
                        } else if (deltaY <= 0) {
                            setState(RESET);
                        }
                    }
                    if (state == RESET) {
                        if (deltaY > 0) {
                            setState(PULL_TO_REFRESH);
                        }
                    }
                    if (state == PULL_TO_REFRESH) {
                        updateHeaderView(-1 * isReadyToRefreshSize() + deltaY);
                    } else if (state == RELEASE_TO_REFRESH) {
                        updateHeaderView(deltaY - isReadyToRefreshSize());
                    }
                    if (isDragForbidden()) {
                        cancelEvent(event);
                        return true;
                    }
                }
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    private void cancelEvent(MotionEvent motionEvent) {
        MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
        cancelEvent.setAction(MotionEvent.ACTION_CANCEL
                | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
        onTouchEvent(cancelEvent);
        cancelEvent.recycle();
    }

    public void setState(int state) {
        this.state = state;
        switch (state) {
            case RELEASE_TO_REFRESH:
                onReleaseToRefresh();
                break;
            case PULL_TO_REFRESH:
                onPullToRefresh();
                break;
            case REFRESHING:
                onRefresh();
                break;
            case RESET:
                onReset();
                break;
            case LOAD_MORE:
                onLoadMore();
                break;
            default:
                break;
        }
    }

    private void onRefresh() {
        onRefreshing();
        if (refreshListener != null) {
            refreshListener.onRefresh();
        }
    }

    protected void onPullToRefresh() {
        Log.d(TAG, "onPullToRefresh");
    }

    protected void onReleaseToRefresh() {
        Log.d(TAG, "onReleaseToRefresh");
    }

    protected void onRefreshing() {
        Log.d(TAG, "onRefreshing");
    }

    protected void onLoadMore() {
        Log.d(TAG, "onLoadMore");
        if (onLoadMoreListener != null) {
            onLoadMoreListener.onLoadMore();
        }
    }

    protected void onReset() {
        Log.d(TAG, "onReset");
    }

    public void onRefreshComplete() {
        Log.i(TAG, "onRefreshComplete");
        setState(RESET);
    }

    public void onRefreshFailed() {
        Log.i(TAG, "onRefreshFailed onReset");
        setState(RESET);
    }

    public void onLoadMoreComplete() {
        Log.i(TAG, "onRefreshComplete");
        setState(RESET);
    }

    public void onLoadMoreFailed() {
        Log.i(TAG, "onRefreshFailed onReset");
        setState(RESET);
    }


    protected void updateHeaderView(int paddingTop) {
    }

    public boolean isDragForbidden() {
        return false;
    }

    protected abstract View getHeaderView();

    protected abstract View getFooterView();

    protected abstract int isReadyToRefreshSize();

    @Override
    public void setOnScrollListener(OnScrollListener l) {
        if (l == onScrollListener) {
            super.setOnScrollListener(l);
        } else {
            otherScrollListener = l;
        }
    }

    @Override
    public void addFooterView(View v) {
        super.addFooterView(v);
        if (onLoadMoreListener != null) {
            removeFooterView(getFooterView());
            addFooterView(getFooterView(), null, false);
        }
    }

    public void setonRefreshListener(OnRefreshListener refreshListener) {
        this.refreshListener = refreshListener;
    }

    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
        this.onLoadMoreListener = onLoadMoreListener;
    }

    public interface OnRefreshListener {
        void onRefresh();
    }

    public interface OnLoadMoreListener {
        void onLoadMore();
    }
}

